package com.zb.security;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.AntUrlPathMatcher;
import org.springframework.security.web.util.UrlMatcher;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.WebApplicationContext;
import com.zb.service.SecurityService;

/**
 *  此类在初始化时，应该取到所有资源及其对应角色的定义。
 *  对于资源的访问权限的定义，我们通过实现FilterInvocationSecurityMetadataSource这个接口来初始化数据。
 * @author zhoubang
 */
public class MyInvocationSecurityMetadataSource implements
        FilterInvocationSecurityMetadataSource {

    @Autowired
    private SecurityService securityService;

    private UrlMatcher urlMatcher = new AntUrlPathMatcher();;
    private static Map<String, Collection<ConfigAttribute>> resourceMap = null;

    /**
     * 资源与角色的对应关系
     * 
     */
    @SuppressWarnings("unchecked")
    private void loadResourceDefine() {
        WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
        ServletContext servletContext = webApplicationContext.getServletContext();
        // 资源与角色映射的map.此map从上下文获取，详情请查看ResourcesRolesListener.java中的操作
        Map<String, String> resRolesMap = (Map<String, String>) servletContext.getAttribute("resRolesMap");
        // 全局保存资源与角色映射
        resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
        // 遍历所有资源.resRolesMap的key为每一个资源的URL
        for (Iterator<String> url = resRolesMap.keySet().iterator(); url.hasNext();) {
            // 获取资源URL
            String resUrl = url.next();
            // 存放一个资源对应的角色列表
            Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
            // 获取该资源所需的角色列表,以","分割的
            String[] role = resRolesMap.get(resUrl).split(",");
            for (int i = 0; i < role.length; i++) {
                // 转换成角色对象
                ConfigAttribute ca = new SecurityConfig(role[i]);
                // 将角色加入权限
                atts.add(ca);
                // 最终保存资源与角色的对应关系
                resourceMap.put(resUrl, atts);
            }
        }
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    /**
     * 根据一个URL,找出该URL的权限配置 该方法是最核心的地方，提供某个资源对应的权限定义，即getAttributes方法返回的结果。
     * 例子中使用的是 AntUrlPathMatcher来检查URL是否与资源定义匹配，事实上你还要用正则的方式来匹配，或者自己实现一个matcher
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object)
            throws IllegalArgumentException {

        // 上下文读取资源与角色映射
        loadResourceDefine();

        String url = ((FilterInvocation) object).getRequestUrl();
        // 获取需要拦截过滤的URL列表
        Iterator<String> ite = resourceMap.keySet().iterator();
        while (ite.hasNext()) {
            // 遍历得到每一个需要过滤的URL
            String resURL = ite.next();
            // 将当前访问的URL(url)与需要过滤的URL(resURL)进行比较，判断是否需要对当前请求的URL进行权限验证
            if (urlMatcher.pathMatchesUrl(resURL, url)) {
                return resourceMap.get(resURL);
            }
        }
        // 如果在所有需要过滤的url中没有匹配到当前url，说明不需要对当前url进行过滤拦截，也不需要进行权限验证，所以就直接放行！页面可以直接访问到了
        return null;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}
